package com.jp.fm.data;

import java.io.IOException;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.Map;

import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;

import org.apache.commons.lang3.builder.ToStringBuilder;
import org.jasypt.util.text.BasicTextEncryptor;

import com.jp.fm.HomeFinanceRunner;

public class UserAccount implements Serializable {

	public static final String CACHE_NAME = "UserAccount";

	private static final String COLUMN_ID = "ID";
	private static final String COLUMN_LASTUPDATE = "LAST_UPDATE";
	private static final String COLUMN_NAME = "NAME";
	private static final String COLUMN_RUN_CLASS = "RUN_CLASS";
	private static final String COLUMN_KEY = "KEY";
	private static final String COLUMN_VALUE = "VALUE";

	public static final String USERNAME = "username";
	public static final String PASSWORD = "password";
	public static final String URL = "url";
	public static final String ACCT_NUMBER = "account_number";
	public static final String ZIP = "zip";

	private static final long serialVersionUID = 1L;

	private Integer id = null;
	private String name = null;
	private String username = null;
	private String password = null;
	private String runClass = null;
	private Timestamp lastupdate = null;
	private Map<String, String> attributes = null;

	public final String getName() {
		return this.name;
	}

	public UserAccount(final String name, final String runClass,
			final Timestamp lastupdate) {

		this.attributes = new HashMap<String, String>();
		this.name = name;
		this.runClass = runClass;
		this.lastupdate = lastupdate;
	}

	public final Timestamp getLastupdate() {
		return this.lastupdate;
	}

	public final Integer getId() {
		return this.id;
	}

	public final void setId(final Integer id) {
		this.id = id;
	}

	public final String getRunClass() {
		return this.runClass;
	}

	public final String getUsername() {
		return this.username;
	}

	public final void setUsername(final String username) {
		this.username = username;
	}

	public final String getPassword() {
		return this.password;
	}

	public final void setPassword(final String password) {
		this.password = password;
	}

	public final String getAttribute(final String key) {

		return this.attributes.get(key);
	}

	public final void setAttribute(final String key, final String value) {

		this.attributes.put(key, value);
	}

	public static UserAccount findByName(final String name) {

		UserAccount acct = null;

		// look in cache first
		Cache cache = HomeFinanceRunner.getCache(CACHE_NAME);
		Element element = cache.get(name);

		if (null == element) {

			Connection c = null;
			PreparedStatement p = null;
			ResultSet rs = null;

			try {
				c = HomeFinanceRunner.getConnection();
				p = c.prepareStatement("select id, name, run_class, last_update "
						+ "from user_account where name = ?");
				p.setString(1, name);
				rs = p.executeQuery();

				// Get main account info
				while (rs.next()) {

					acct = new UserAccount(rs.getString(COLUMN_NAME),
							rs.getString(COLUMN_RUN_CLASS),
							rs.getTimestamp(COLUMN_LASTUPDATE));
					acct.setId(rs.getInt(COLUMN_ID));

					cache.put(new Element(acct.getName(), acct));
				}
				// Get attributes
				if (null != acct) {
					findAttributes(acct);
				}
			} catch (SQLException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				HomeFinanceRunner.closeConnection(c, p, rs);
			}
		} else {
			acct = (UserAccount) element.getObjectValue();
		}
		return acct;
	}

	public static void findAttributes(final UserAccount uAcct) {

		Connection c = null;
		PreparedStatement p = null;
		ResultSet rs = null;
		Site site = Site.findSite();
		BasicTextEncryptor textEncryptor = new BasicTextEncryptor();
		textEncryptor.setPassword(site.getSiteKey());

		try {
			c = HomeFinanceRunner.getConnection();
			p = c.prepareStatement("select id, key, value, last_update "
					+ "from user_account_nvp where user_account_id = ?");
			p.setInt(1, uAcct.getId());
			rs = p.executeQuery();

			while (rs.next()) {

				String key = rs.getString(COLUMN_KEY);
				String value = rs.getString(COLUMN_VALUE);

				// If site is encrypted, decrypt the values
				if (Site.findSite().isDbEncrypted()) {
					key = textEncryptor.decrypt(key);
					value = textEncryptor.decrypt(value);
				}

				if (USERNAME.equals(key)) {
					uAcct.setUsername(value);
				} else if (PASSWORD.equals(key)) {
					uAcct.setPassword(value);
				} else {
					uAcct.setAttribute(key, value);
				}
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			HomeFinanceRunner.closeConnection(c, p, rs);
		}
	}

	public static UserAccount findByRunClass(final String className) {

		UserAccount acct = null;

		// look in cache first
		Cache cache = HomeFinanceRunner.getCache(CACHE_NAME);
		Element element = cache.get(className);

		if (null == element) {

			Connection c = null;
			PreparedStatement p = null;
			ResultSet rs = null;

			try {
				c = HomeFinanceRunner.getConnection();
				p = c.prepareStatement("select id, name, run_class, last_update "
						+ "from user_account where run_class = ?");
				p.setString(1, className);
				rs = p.executeQuery();

				// Get main account info
				while (rs.next()) {

					acct = new UserAccount(rs.getString(COLUMN_NAME),
							rs.getString(COLUMN_RUN_CLASS),
							rs.getTimestamp(COLUMN_LASTUPDATE));
					acct.setId(rs.getInt(COLUMN_ID));

					cache.put(new Element(className, acct));
				}
				// Get attributes
				if (null != acct) {
					findAttributes(acct);
				}
			} catch (SQLException e) {

				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				HomeFinanceRunner.closeConnection(c, p, rs);
			}
		} else {
			acct = (UserAccount) element.getObjectValue();
		}
		return acct;
	}

	@Override
	public final String toString() {

		return new ToStringBuilder(this).append("id = " + this.id)
				.append("name = " + this.name)
				.append("lastupdate = " + this.lastupdate).toString();
	}

}
